/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author         Notes
 * 2019-07-28     Ernest         the first version
 * 2024-09-08     SliverWang     modify
 */

#include "board.h"
#include "drv_wm8978.h"
#include "drv_sound.h"
#include "drv_gpio.h"

#if defined(RT_USING_AUDIO)

#define DBG_TAG "drv.sound"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>

static drv_i2s_t drv_i2s;

static void i2s2_tx_dma_isr(void)
{
    if (dma_flag_get(AUDIO_TX_DMA_HDT_FLAG) == SET)
    {
        rt_audio_tx_complete(&drv_i2s.audio);
        dma_flag_clear(AUDIO_TX_DMA_HDT_FLAG);
    }

    if (dma_flag_get(AUDIO_TX_DMA_FDT_FLAG) == SET)
    {
        rt_audio_tx_complete(&drv_i2s.audio);
        dma_flag_clear(AUDIO_TX_DMA_FDT_FLAG);
    }
}

void AUDIO_TX_DMA_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    i2s2_tx_dma_isr();

    /* leave interrupt */
    rt_interrupt_leave();
}

static void i2s2_rx_dma_isr(void)
{
    if (dma_flag_get(AUDIO_RX_DMA_HDT_FLAG) == SET)
    {
        dma_flag_clear(AUDIO_RX_DMA_HDT_FLAG);
        rt_audio_rx_done(&drv_i2s.audio, &drv_i2s.rx_buff[0], drv_i2s.rx_size / 2);
    }

    if (dma_flag_get(AUDIO_RX_DMA_FDT_FLAG) == SET)
    {
        dma_flag_clear(AUDIO_RX_DMA_FDT_FLAG);
        rt_audio_rx_done(&drv_i2s.audio, &drv_i2s.rx_buff[drv_i2s.rx_size / 2], drv_i2s.rx_size / 2);
    }
}

void AUDIO_RX_DMA_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    i2s2_rx_dma_isr();

    /* leave interrupt */
    rt_interrupt_leave();
}


static rt_err_t drv_i2s_transfer_stop(void)
{
    nvic_irq_disable(AUDIO_TX_DMA_IRQ);
    nvic_irq_disable(AUDIO_RX_DMA_IRQ);

    dma_channel_enable(AUDIO_TX_DMA_CHANNEL, FALSE);
    dma_channel_enable(AUDIO_RX_DMA_CHANNEL, FALSE);

    dma_interrupt_enable(AUDIO_TX_DMA_CHANNEL, DMA_FDT_INT | DMA_HDT_INT, FALSE);
    dma_interrupt_enable(AUDIO_RX_DMA_CHANNEL, DMA_FDT_INT | DMA_HDT_INT, FALSE);

    spi_i2s_dma_transmitter_enable(AUDIO_TX_Handle, FALSE);
    spi_i2s_dma_receiver_enable(AUDIO_RX_Handle, FALSE);

    i2s_enable(AUDIO_TX_Handle, FALSE);
    i2s_enable(AUDIO_RX_Handle, FALSE);

    return RT_EOK;
}

static rt_err_t drv_i2s_transfer_start(void)
{
    dma_init_type dma_init_struct;

    drv_i2s_transfer_stop();

    wm8978_play_recoder_start(drv_i2s.i2c_bus);

    dma_default_para_init(&dma_init_struct);
    dma_init_struct.buffer_size = drv_i2s.tx_size / 2;
    dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
    dma_init_struct.memory_inc_enable = TRUE;
    dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
    dma_init_struct.peripheral_inc_enable = FALSE;
    dma_init_struct.priority = DMA_PRIORITY_HIGH;
    dma_init_struct.loop_mode_enable = TRUE;

    dma_init_struct.memory_base_addr = (uint32_t)drv_i2s.tx_buff;
    dma_init_struct.peripheral_base_addr = (uint32_t) & (AUDIO_TX_Handle->dt);
    dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
    dma_init(AUDIO_TX_DMA_CHANNEL, &dma_init_struct);

    dma_interrupt_enable(AUDIO_TX_DMA_CHANNEL, DMA_FDT_INT | DMA_HDT_INT, TRUE);
    nvic_irq_enable(AUDIO_TX_DMA_IRQ, 0x03, 0);

    dma_init_struct.memory_base_addr = (uint32_t)drv_i2s.rx_buff;
    dma_init_struct.peripheral_base_addr = (uint32_t) & (AUDIO_RX_Handle->dt);
    dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
    dma_init(AUDIO_RX_DMA_CHANNEL, &dma_init_struct);

    dma_interrupt_enable(AUDIO_RX_DMA_CHANNEL, DMA_FDT_INT | DMA_HDT_INT, TRUE);
    nvic_irq_enable(AUDIO_RX_DMA_IRQ, 0x04, 0);

    dma_channel_enable(AUDIO_TX_DMA_CHANNEL, TRUE);
    dma_channel_enable(AUDIO_RX_DMA_CHANNEL, TRUE);

    spi_i2s_dma_transmitter_enable(AUDIO_TX_Handle, TRUE);
    spi_i2s_dma_receiver_enable(AUDIO_RX_Handle, TRUE);

    i2s_enable(AUDIO_TX_Handle, TRUE);
    i2s_enable(AUDIO_RX_Handle, TRUE);


    return RT_EOK;
}


static rt_err_t drv_i2s_init(struct rt_audio_device *audio)
{
    RT_ASSERT(audio != RT_NULL);
    drv_i2s_t *drv_audio = (drv_i2s_t *)audio->parent.user_data;
    i2s_init_type i2s_init_struct;

    drv_i2s.i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(CODEC_I2C_NAME);
    if (drv_i2s.i2c_bus == NULL)
    {
        return -RT_EIO;
    }

    wm8978_init(drv_i2s.i2c_bus);

    crm_periph_clock_enable(AUDIO_TX_DMA_CLOCK, TRUE);
    crm_periph_clock_enable(AUDIO_RX_DMA_CLOCK, TRUE);

    dma_reset(AUDIO_TX_DMA_CHANNEL);
    dma_reset(AUDIO_RX_DMA_CHANNEL);

    dmamux_enable(DMA1, TRUE);
    dmamux_init(AUDIO_TX_DMA_MUX_CHANNEL, AUDIO_TX_DMA_REQ_ID);
    dmamux_init(AUDIO_RX_DMA_MUX_CHANNEL, AUDIO_RX_DMA_REQ_ID);

    at32_msp_i2s2_init(NULL);

    spi_i2s_reset(AUDIO_TX_Handle);
    spi_i2s_reset(AUDIO_RX_Handle);

    i2s_default_para_init(&i2s_init_struct);
    i2s_init_struct.audio_protocol = I2S_AUDIO_PROTOCOL_PHILLIPS;
    i2s_init_struct.data_channel_format = I2S_DATA_16BIT_CHANNEL_16BIT;
    i2s_init_struct.mclk_output_enable = TRUE;
    i2s_init_struct.audio_sampling_freq = AUDIO_FREQUENCY_016K;
    i2s_init_struct.clock_polarity = I2S_CLOCK_POLARITY_LOW;

    i2s_init_struct.operation_mode = I2S_MODE_MASTER_TX;
    i2s_init(AUDIO_TX_Handle, &i2s_init_struct);

    i2s_init_struct.operation_mode = I2S_MODE_SLAVE_RX;
    i2s_init_struct.mclk_output_enable = FALSE;
    i2s_init_struct.audio_sampling_freq = I2S_AUDIO_FREQUENCY_DEFAULT;
    i2s_init(AUDIO_RX_Handle, &i2s_init_struct);

    drv_audio->audio_config.samplerate = AUDIO_FREQUENCY_016K;
    drv_audio->audio_config.samplebits = 16;
    drv_audio->audio_config.channels = 2;

    return RT_EOK;
}

static rt_err_t drv_i2s_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    RT_ASSERT(audio != RT_NULL);
    drv_i2s_t *drv_audio = (drv_i2s_t *)audio->parent.user_data;

    switch (caps->main_type)
    {
        case AUDIO_TYPE_INPUT:
        {
            switch (caps->sub_type)
            {
                case AUDIO_DSP_PARAM:
                {
                    caps->udata.config.channels = drv_audio->audio_config.channels;
                    caps->udata.config.samplebits = drv_audio->audio_config.samplebits;
                    caps->udata.config.samplerate = drv_audio->audio_config.samplerate;
                }
                break;
                case AUDIO_DSP_SAMPLERATE:
                {
                    caps->udata.config.samplerate = drv_audio->audio_config.samplerate;
                }
                break;
                case AUDIO_DSP_CHANNELS:
                {
                    caps->udata.config.channels = drv_audio->audio_config.channels;
                }
                break;
                case AUDIO_DSP_SAMPLEBITS:
                {
                    caps->udata.config.samplebits = drv_audio->audio_config.samplebits;
                }
                break;
                default:
                {
                    result = -RT_ERROR;
                }
                break;
            }
        }
        break;
        case AUDIO_TYPE_OUTPUT:
        {
            switch (caps->sub_type)
            {
                case AUDIO_DSP_PARAM:
                {
                    caps->udata.config.samplerate = drv_audio->audio_config.samplerate;
                    caps->udata.config.channels = drv_audio->audio_config.channels;
                    caps->udata.config.samplebits = drv_audio->audio_config.samplebits;
                }
                break;
                case AUDIO_DSP_SAMPLERATE:
                {
                    caps->udata.config.samplerate = drv_audio->audio_config.samplerate;
                }
                break;
                case AUDIO_DSP_CHANNELS:
                {
                    caps->udata.config.channels = drv_audio->audio_config.channels;
                }
                break;
                case AUDIO_DSP_SAMPLEBITS:
                {
                    caps->udata.config.samplebits = drv_audio->audio_config.samplebits;
                }
                break;
                default:
                {
                    result = -RT_ERROR;
                }
                break;
            }
        }
        break;
        default:
            result = -RT_ERROR;
            break;
    }

    return result;
}

static rt_err_t drv_i2s_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    RT_ASSERT(audio != RT_NULL);
    drv_i2s_t *drv_audio = (drv_i2s_t *)audio->parent.user_data;

    switch(caps->main_type)
    {
        case AUDIO_TYPE_OUTPUT:
        {
            switch(caps->sub_type)
            {
                case AUDIO_DSP_PARAM:
                {
                    drv_audio->audio_config.samplerate = caps->udata.config.samplerate;
                    drv_audio->audio_config.samplebits = caps->udata.config.samplebits;
                    drv_audio->audio_config.channels = caps->udata.config.channels;
                }
                break;
                case AUDIO_DSP_SAMPLERATE:
                {
                    drv_audio->audio_config.samplerate = caps->udata.config.samplerate;
                }
                break;
                case AUDIO_DSP_CHANNELS:
                {
                    drv_audio->audio_config.channels = caps->udata.config.channels;
                }
                break;
                case AUDIO_DSP_SAMPLEBITS:
                {
                    drv_audio->audio_config.samplebits = caps->udata.config.samplebits;
                }
                break;
                default:
                {
                    result = -RT_ERROR;
                }
                break;
            }
        }
        break;
        case AUDIO_TYPE_INPUT:
        {
            switch(caps->sub_type)
            {
                case AUDIO_DSP_PARAM:
                {
                    drv_audio->audio_config.samplerate = caps->udata.config.samplerate;
                    drv_audio->audio_config.channels   = caps->udata.config.channels;
                    drv_audio->audio_config.samplebits = caps->udata.config.samplebits;
                }
                break;
                case AUDIO_DSP_SAMPLERATE:
                {
                    drv_audio->audio_config.samplerate = caps->udata.config.samplerate;
                }
                break;
                case AUDIO_DSP_CHANNELS:
                {
                    drv_audio->audio_config.channels = caps->udata.config.channels;
                }
                break;
                case AUDIO_DSP_SAMPLEBITS:
                {
                    drv_audio->audio_config.samplebits = caps->udata.config.samplebits;
                }
                break;
                default:
                {
                    result = -RT_ERROR;
                }
                break;
            }
        }
        break;
        case AUDIO_TYPE_MIXER:
        {

        }
        break;
        default:
            break;
    }

    return result;
}

static rt_err_t drv_i2s_start(struct rt_audio_device *audio, int stream)
{
    RT_ASSERT(audio != RT_NULL);
    drv_i2s_t *drv_audio = (drv_i2s_t *)audio->parent.user_data;

    if (drv_audio->is_running == RT_FALSE)
    {
        drv_i2s_transfer_start();
        drv_audio->is_running = RT_TRUE;
    }

    return RT_EOK;
}

static rt_err_t drv_i2s_stop(struct rt_audio_device *audio, int stream)
{
    RT_ASSERT(audio != RT_NULL);
    drv_i2s_t *drv_audio = (drv_i2s_t *)audio->parent.user_data;

    if (drv_audio->is_running != RT_FALSE)
    {
        drv_i2s_transfer_stop();
        drv_audio->is_running = RT_FALSE;
    }

    return RT_EOK;
}

static void drv_i2s_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
    /**
     *               TX_FIFO
     * +----------------+----------------+
     * |     block1     |     block2     |
     * +----------------+----------------+
     *  \  block_size  /
     */
    info->buffer = drv_i2s.tx_buff;
    info->total_size = drv_i2s.tx_size;
    info->block_size = drv_i2s.tx_size / 2;
    info->block_count = 2;
}

static struct rt_audio_ops drv_i2s_ops =
{
    .getcaps = drv_i2s_getcaps,
    .configure = drv_i2s_configure,
    .init = drv_i2s_init,
    .start = drv_i2s_start,
    .stop = drv_i2s_stop,
    .transmit = RT_NULL,
    .buffer_info = drv_i2s_buffer_info,
};

int rt_hw_sound_init(void)
{
    drv_i2s.tx_buff = rt_calloc(1, DRV_I2S_BUFF_DEFAULT_SIZE);
    if (drv_i2s.tx_buff == RT_NULL)
    {
        return -RT_ENOMEM;
    }

    drv_i2s.rx_buff = rt_calloc(1, DRV_I2S_BUFF_DEFAULT_SIZE);
    if (drv_i2s.rx_buff == RT_NULL)
    {
        return -RT_ENOMEM;
    }

    drv_i2s.tx_size = DRV_I2S_BUFF_DEFAULT_SIZE;
    drv_i2s.rx_size = DRV_I2S_BUFF_DEFAULT_SIZE;

    /* register sound device */
    drv_i2s.audio.ops = &drv_i2s_ops;
    rt_audio_register(&drv_i2s.audio, "sound0", RT_DEVICE_FLAG_RDWR, &drv_i2s);

    return RT_EOK;
}

//INIT_DEVICE_EXPORT(rt_hw_sound_init);

#endif
